#include "yltkDynamicLoader.h"
#include <ctype.h>



// This file is actually 3 different implementations.
// 1. HP machines which uses shl_load
// 2. Mac OS X 10.2.x and earlier which uses NSLinkModule
// 3. Windows which uses LoadLibrary
// 4. Most unix systems (including Mac OS X 10.3 and later) which use dlopen
// (default) Each part of the ifdef contains a complete implementation for
// the static methods of DynamicLoader.

// ---------------------------------------------------------------
// 1. Implementation for HPUX  machines
#ifdef __hpux
#include <errno.h>
#include <dl.h>
#define DYNAMICLOADER_DEFINED 1

namespace yltk{
	DynamicLoader::DynamicLoader()
	{
	}

	//----------------------------------------------------------------------------
	DynamicLoader::~DynamicLoader()
	{
	}

	//----------------------------------------------------------------------------
	LibHandle DynamicLoader::OpenLibrary(const char* libname )
	{
		 return shl_load(libname, BIND_DEFERRED | DYNAMIC_PATH, 0L);
	}

	//----------------------------------------------------------------------------
	int DynamicLoader::CloseLibrary(LibHandle lib)
	{
		return !shl_unload(lib);
	}

	//----------------------------------------------------------------------------
	//itkSymbolPointer
	void* DynamicLoader::GetSymbolAddress(LibHandle lib, const char* sym)
	{
		void* addr;
		int status;

		/* TYPE_PROCEDURE Look for a function or procedure. (This used to be default)
		* TYPE_DATA      Look for a symbol in the data segment (for example, variables).
		* TYPE_UNDEFINED Look for any symbol.
		*/
		status = shl_findsym (&lib, sym, TYPE_UNDEFINED, &addr);
		void* result = (status < 0) ? (void*)0 : addr;

		// Hack to cast pointer-to-data to pointer-to-function.
		return *reinterpret_cast<DynamicLoader::SymbolPointer*>(&result);
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LibPrefix()
	{
		 return "lib";
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LibExtension()
	{
		return ".sl";
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LastError()
	{
		// TODO: Need implementation with errno/strerror
		/* If successful, shl_findsym returns an integer (int) value zero. If
		* shl_findsym cannot find sym, it returns -1 and sets errno to zero.
		* If any other errors occur, shl_findsym returns -1 and sets errno to one
		* of these values (defined in <errno.h>):
		* ENOEXEC
		* A format error was detected in the specified library.
		* ENOSYM
		* A symbol on which sym depends could not be found.
		* EINVAL
		* The specified handle is invalid.
		*/

		if(  errno == ENOEXEC
			|| errno == ENOSYM
			|| errno == EINVAL )
		{
			return strerror(errno);
		}
		// else
		return 0;
	}
}

#endif //__hpux


// ---------------------------------------------------------------
// 2. Implementation for Mac OS X 10.2.x and earlier
#ifdef __APPLE__
#if MAC_OS_X_VERSION_MAX_ALLOWED < 1030
#include <string.h> // for strlen
#include <mach-o/dyld.h>
#define DYNAMICLOADER_DEFINED 1

namespace yltk{
	DynamicLoader::DynamicLoader()
	{
	}

	//----------------------------------------------------------------------------
	DynamicLoader::~DynamicLoader()
	{
	}

	//----------------------------------------------------------------------------
	LibHandle DynamicLoader::OpenLibrary(const char* libname )
	{
		NSObjectFileImageReturnCode rc;
		NSObjectFileImage image = 0;

		rc = NSCreateObjectFileImageFromFile(libname, &image);
		// rc == NSObjectFileImageInappropriateFile when trying to load a dylib file
		if( rc != NSObjectFileImageSuccess )
		{
			return 0;
		}
		NSModule handle = NSLinkModule(image, libname,
			NSLINKMODULE_OPTION_BINDNOW|NSLINKMODULE_OPTION_RETURN_ON_ERROR);
		NSDestroyObjectFileImage(image);
		return handle;
	}

	//----------------------------------------------------------------------------
	int DynamicLoader::CloseLibrary(LibHandle lib)
	{
		// NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED
		// With  this  option  the memory for the module is not deallocated
		// allowing pointers into the module to still be valid.
		// You should use this option instead if your code experience some problems
		// reported against Panther 10.3.9 (fixed in Tiger 10.4.2 and up)
		bool success = NSUnLinkModule(lib, NSUNLINKMODULE_OPTION_NONE);
		return success;
	}

	//----------------------------------------------------------------------------
	//itkSymbolPointer
	void* DynamicLoader::GetSymbolAddress(LibHandle lib, const char* sym)
	{
		void *result=0;
		// Need to prepend symbols with '_' on Apple-gcc compilers
		size_t len = strlen(sym);
		char *rsym = new char[len + 1 + 1];
		strcpy(rsym, "_");
		strcat(rsym+1, sym);

		NSSymbol symbol = NSLookupSymbolInModule(lib, rsym);
		if(symbol)
		{
			result = NSAddressOfSymbol(symbol);
		}

		delete[] rsym;
		// Hack to cast pointer-to-data to pointer-to-function.
		return *reinterpret_cast<DynamicLoader::SymbolPointer*>(&result);
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LibPrefix()
	{
		return "lib";
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LibExtension()
	{
		 return ".so";
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LastError()
	{
		return 0;
	}
}

#endif
#endif //__APPLE__

// ---------------------------------------------------------------
// 3. Implementation for Windows win32 code but not cygwin
#if defined(_WIN32) && !defined(__CYGWIN__)
#include <windows.h>
#define DYNAMICLOADER_DEFINED 1

namespace yltk{
	DynamicLoader::DynamicLoader()
	{
	}

	//----------------------------------------------------------------------------
	DynamicLoader::~DynamicLoader()
	{
	}

	//----------------------------------------------------------------------------
	LibHandle DynamicLoader::OpenLibrary(const char* libname )
	{
		LibraryHandle lh;
#ifdef UNICODE
		wchar_t libn[MB_CUR_MAX];
		mbstowcs(libn, libname, MB_CUR_MAX);
		lh = LoadLibrary(libn);
#else
		lh = LoadLibrary(libname);
#endif
		return lh;
	}

	//----------------------------------------------------------------------------
	int DynamicLoader::CloseLibrary(LibHandle lib)
	{
		return (int)FreeLibrary(lib);
	}

	//----------------------------------------------------------------------------
	//itkSymbolPointer
	void* DynamicLoader::GetSymbolAddress(LibHandle lib, const char* sym)
	{
		// TODO: The calling convention affects the name of the symbol.  We
		// should have a tool to help get the symbol with the desired
		// calling convention.  Currently we assume cdecl.
		//
		// Borland:
		//   __cdecl    = "_func" (default)
		//   __fastcall = "@_func"
		//   __stdcall  = "func"
		//
		// Watcom:
		//   __cdecl    = "_func"
		//   __fastcall = "@_func@X"
		//   __stdcall  = "_func@X"
		//   __watcall  = "func_" (default)
		//
		// MSVC:
		//   __cdecl    = "func" (default)
		//   __fastcall = "@_func@X"
		//   __stdcall  = "_func@X"
		//
		// Note that the "@X" part of the name above is the total size (in
		// bytes) of the arguments on the stack.
		void *result;
		#if defined(__BORLANDC__) || defined(__WATCOMC__)
				// Need to prepend symbols with '_'
				size_t len = strlen(sym);
				char *rsym = new char[len + 1 + 1];
				strcpy(rsym, "_");
				strcat(rsym, sym);
		#else
				const char *rsym = sym;
		#endif
		#ifdef UNICODE
				wchar_t wsym[MB_CUR_MAX];
				mbstowcs(wsym, rsym, MB_CUR_MAX);
				result = GetProcAddress(lib, wsym);
		#else
				result = (void*)GetProcAddress(lib, rsym);
		#endif
		#if defined(__BORLANDC__) || defined(__WATCOMC__)
				delete[] rsym;
		#endif
				// Hack to cast pointer-to-data to pointer-to-function.
		#ifdef __WATCOMC__
				return *(DynamicLoader::SymbolPointer*)(&result);
		#else
				return *reinterpret_cast<SymbolPointer*>(&result);
		#endif
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LibPrefix()
	{
		#ifdef __MINGW32__
				return "lib";
		#else
				return "";
		#endif
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LibExtension()
	{
		return ".dll";
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LastError()
	{
		LPVOID lpMsgBuf=NULL;

		FormatMessage(
			FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
			NULL,
			GetLastError(),
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			(LPTSTR) &lpMsgBuf,
			0,
			NULL
			);

		if(!lpMsgBuf)
		{
			return NULL;
		}

		static char* str = 0;
		delete [] str;
		str = strcpy(new char[strlen((char*)lpMsgBuf)+1], (char*)lpMsgBuf);
		// Free the buffer.
		LocalFree( lpMsgBuf );
		
		return str;
	}
}

#endif //_WIN32


// ---------------------------------------------------------------
// 4. Implementation for BeOS
#if defined __BEOS__

#include <string.h> // for strerror()

#include <be/kernel/image.h>
#include <be/support/Errors.h>

#define DYNAMICLOADER_DEFINED 1

namespace yltk{

	static image_id last_dynamic_err = B_OK;

	DynamicLoader::DynamicLoader()
	{
	}

	//----------------------------------------------------------------------------
	DynamicLoader::~DynamicLoader()
	{
	}

	//----------------------------------------------------------------------------
	LibHandle DynamicLoader::OpenLibrary(const char* libname )
	{
		// image_id's are integers, errors are negative. Add one just in case we
		//  get a valid image_id of zero (is that even possible?).
		image_id rc = load_add_on(libname);
		if (rc < 0)
		{
			last_dynamic_err = rc;
			return 0;
		}

		return rc+1;
	}

	//----------------------------------------------------------------------------
	int DynamicLoader::CloseLibrary(LibHandle lib)
	{
		if (!lib)
		{
			last_dynamic_err = B_BAD_VALUE;
			return 0;
		}
		else
		{
			// The function dlclose() returns 0 on success, and non-zero on error.
			status_t rc = unload_add_on(lib-1);
			if (rc != B_OK)
			{
				last_dynamic_err = rc;
				return 0;
			}
		}

		return 1;
	}

	//----------------------------------------------------------------------------
	//itkSymbolPointer
	void* DynamicLoader::GetSymbolAddress(LibHandle lib, const char* sym)
	{
		// Hack to cast pointer-to-data to pointer-to-function.
		union 
		{
			void* pvoid;
			DynamicLoader::SymbolPointer psym;
		} result;

		result.psym = NULL;

		if (!lib)
		{
			last_dynamic_err = B_BAD_VALUE;
		}
		else
		{
			// !!! FIXME: BeOS can do function-only lookups...does this ever
			// !!! FIXME:  actually _want_ a data symbol lookup, or was this union
			// !!! FIXME:  a leftover of dlsym()? (s/ANY/TEXT for functions only).
			status_t rc = get_image_symbol(lib-1,sym,B_SYMBOL_TYPE_ANY,&result.pvoid);
			if (rc != B_OK)
			{
				last_dynamic_err = rc;
				result.psym = NULL;
			}
		}
		return result.psym;
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LibPrefix()
	{
		return "lib";
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LibExtension()
	{
		return ".so";
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LastError()
	{
		const char *retval = strerror(last_dynamic_err);
		last_dynamic_err = B_OK;
		return retval;
	}
}

#endif //__BEOS__

// ---------------------------------------------------------------
// 5. Implementation for systems without dynamic libs
// __gnu_blrts__ is IBM BlueGene/L
// __LIBCATAMOUNT__ is defined on Catamount on Cray compute nodes
#if defined(__gnu_blrts__) || defined(__LIBCATAMOUNT__)
#include <string.h> // for strerror()
#define DYNAMICLOADER_DEFINED 1

namespace yltk{
	DynamicLoader::DynamicLoader()
	{
	}

	//----------------------------------------------------------------------------
	DynamicLoader::~DynamicLoader()
	{
	}

	//----------------------------------------------------------------------------
	LibHandle DynamicLoader::OpenLibrary(const char* libname )
	{
		return 0;
	}

	//----------------------------------------------------------------------------
	int DynamicLoader::CloseLibrary(LibHandle lib)
	{
		if (!lib)
		{
			return 0;
		}

		return 1;
	}

	//----------------------------------------------------------------------------
	//itkSymbolPointer
	void* DynamicLoader::GetSymbolAddress(LibHandle lib, const char* sym)
	{
		return 0;
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LibPrefix()
	{
		return "lib";
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LibExtension()
	{
		return ".a";
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LastError()
	{
		 return "General error";
	}
}

#endif //__gnu_blrts__

// ---------------------------------------------------------------
// 6. Implementation for default UNIX machines.
// if nothing has been defined then use this
#ifndef DYNAMICLOADER_DEFINED
#define DYNAMICLOADER_DEFINED 1
// Setup for most unix machines
#include <dlfcn.h>

namespace yltk{
	DynamicLoader::DynamicLoader()
	{
	}

	//----------------------------------------------------------------------------
	DynamicLoader::~DynamicLoader()
	{
	}

	//----------------------------------------------------------------------------
	LibHandle DynamicLoader::OpenLibrary(const char* libname )
	{
		return dlopen(libname, RTLD_LAZY);
	}

	//----------------------------------------------------------------------------
	int DynamicLoader::CloseLibrary(LibHandle lib)
	{
		if (lib)
		{
			// The function dlclose() returns 0 on success, and non-zero on error.
			return !dlclose(lib);
		}
		// else
		return 0;
	}

	//----------------------------------------------------------------------------
	//itkSymbolPointer
	void* DynamicLoader::GetSymbolAddress(LibHandle lib, const char* sym)
	{
		// Hack to cast pointer-to-data to pointer-to-function.
		union 
		{
			void* pvoid;
			DynamicLoader::SymbolPointer psym;
		} result;
		result.pvoid = dlsym(lib, sym);
		return result.psym;
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LibPrefix()
	{
		return "lib";
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LibExtension()
	{
		#ifdef __CYGWIN__
				return ".dll";
		#else
				return ".so";
		#endif
	}

	//----------------------------------------------------------------------------
	const char* DynamicLoader::LastError()
	{
		return dlerror();
	}
}

#endif 